#include "pincfg.h"
#include "nor_cmd.h"
#include "bsp_qpi_nor.h"
#include "lib_surport.h"
#include "bsp_qpi_opt.h"

#define spi_nor_ms_delay lib_delay_ms
#define spi_nor_us_delay lib_delay_us

/*****************************************************************************
* @brief   hal
*****************************************************************************/
void hal_snor_spi_cs(uint8_t vol)
{
	if (vol)
	{
		snor_ss.en();
	}
	else
	{
		snor_ss.dis();
	}

	NOP4();
}

/*****************************************************************************
* @brief   auto cmd.
*****************************************************************************/
void bsp_snor_cmd_exe(uint8_t cmd)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);
	bsp_qpi_cmd_exe(cmd);
	hal_snor_spi_cs(1);
}

//cmd with one par send define
void bsp_snor_cmd_set(uint8_t cmd, uint8_t par)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);
	bsp_qpi_cmd_set(cmd, par);
	hal_snor_spi_cs(1);
}

uint8_t bsp_snor_cmd_get(uint8_t cmd)
{
	uint8_t rd;

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);
	rd = bsp_qpi_cmd_get(cmd);
	hal_snor_spi_cs(1);

	return rd;
}

void bsp_snor_cmd_aset(uint8_t cmd, uint8_t addr, uint8_t par)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);
	bsp_qpi_cmd_aset(cmd, addr, par);
	hal_snor_spi_cs(1);
}

uint8_t bsp_snor_cmd_aget(uint8_t cmd, uint8_t addr)
{
	uint8_t rd;

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);
	rd = bsp_qpi_cmd_aget(cmd, addr);
	hal_snor_spi_cs(1);

	return rd;
}

void bsp_snor_cmd_auto_set(uint8_t cmd, const uint8_t *buff, uint8_t cnt)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(cmd, 0, QSPI_CMD_ADRLEN_0_BYTE, buff, cnt);
	hal_snor_spi_cs(1);
}

void bsp_snor_cmd_auto_get(uint8_t cmd, uint8_t *buff, uint8_t cnt)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_read(cmd, 0, QSPI_CMD_ADRLEN_0_BYTE, buff, cnt);
	hal_snor_spi_cs(1);
}

/*-------------------------------------------------------------*
* reset
*--------------------------------------------------------------*/
void bsp_snor_reset(void)
{
	bsp_snor_cmd_exe(SPI_NOR_CMD_Enable_Reset);
	bsp_snor_cmd_exe(SPI_NOR_CMD_Reset);
	bsp_snor_wait_for_busy();
}

/*****************************************************************************
* @brief   read reg.
*****************************************************************************/
uint8_t bsp_snor_reg_get(char reg)
{
	uint16_t cnt;
	uint8_t st[2], cmd;

	// reg select
	switch (reg)
	{
	case 1:
		cmd = SPI_NOR_CMD_Read_StReg1;
		break;
	case 2:
		cmd = SPI_NOR_CMD_Read_StReg2;
		break;
	case 3:
		cmd = SPI_NOR_CMD_Read_StReg3;
		break;
	default:
		return 0xFF;
	}

	// init
	st[0] = 0x55;
	st[1] = ~0x55;

	// read
	cnt = 10;
	while (cnt--)
	{
		st[cnt & 0x01] = bsp_snor_cmd_get(cmd);

		//判定有效性
		if (st[0] == st[1])
		{
			break;
		}
	}
	return st[0];
}

/*****************************************************************************
* @brief   set reg.
*****************************************************************************/
uint8_t bsp_snor_reg_set(char reg, uint8_t msk, uint8_t val)
{
	uint16_t cnt;
	uint8_t st_wr, st_rd, cmd;

	// reg select
	switch (reg)
	{
	case 1:
		cmd = SPI_NOR_CMD_Write_StReg1;
		break;
	case 2:
		cmd = SPI_NOR_CMD_Write_StReg2;
		break;
	case 3:
		cmd = SPI_NOR_CMD_Write_StReg3;
		break;
	default:
		return 0xFF;
	}

	// 位掩蔽
	val &= msk;
	// 如果数据已匹配直接退出
	st_wr = bsp_snor_reg_get(reg);
	if ((st_wr & msk) == val)
		return st_wr;
	// 设置位
	st_wr &= ~msk;
	st_wr |= val;

	cnt = 10;
	while (cnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_SRWrite_Enable);

		bsp_snor_cmd_set(cmd, st_wr);

		bsp_snor_wait_for_busy();

		// read_back
		st_rd = bsp_snor_reg_get(reg);

		// verify
		if ((st_rd & msk) == val)
		{
			break;
		}
	}

	return st_rd;
}

/*****************************************************************************
* @brief   read status.
*               The Status Register may be read at any time,
*               even while a Program, Erase or Write Status Register cycle is in progress.
*****************************************************************************/
uint8_t bsp_snor_base_status_read(void)
{
	uint16_t cnt;
	uint8_t st[2];

	st[0] = 0x55;
	st[1] = ~0x55;

	cnt = 10;
	while (cnt--)
	{
		st[cnt & 0x01] = bsp_snor_cmd_get(SPI_NOR_CMD_Read_StReg1);

		//判定有效性
		if (st[0] == st[1])
		{
			break;
		}
	}
	return st[0];
}

/*****************************************************************************
* @brief   read busy statues.
*****************************************************************************/
uint8_t bsp_snor_busy(void)
{
	uint8_t flash_st;

	//读取芯片忙状态
	flash_st = bsp_snor_base_status_read();

	return ((uint8_t)(flash_st & SPI_NOR_ST_BUSY_MASK));
}

/*****************************************************************************
* @brief   wait busy statues.
*****************************************************************************/
uint8_t bsp_snor_wait_for_busy(void)
{
	uint32_t time_out_cnt;
	uint32_t St;

	St = 1;
	//waiting for idle
	time_out_cnt = 500;
	while (bsp_snor_busy() && (time_out_cnt))
	{
		spi_nor_us_delay(200);
		time_out_cnt--;
	}

	if (!time_out_cnt)
		St = 0;

	return St;
}

uint8_t bsp_snor_wait_for_erase(uint32_t time_out_cnt)
{
	uint32_t St;

	St = 1;
	time_out_cnt >>= 2;
	//waiting for idle
	while (bsp_snor_busy() && (time_out_cnt))
	{
		spi_nor_ms_delay(4);
		time_out_cnt--;
	}

	if (!time_out_cnt)
		St = 0;

	return St;
}

/*****************************************************************************
* @brief   write enable.
*****************************************************************************/
void bsp_snor_write_enable(void)
{
	uint16_t cnt;

	cnt = 10;
	while (cnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_Write_Enable);

		if (bsp_snor_base_status_read() & SPI_NOR_ST_WRITE_EN)
		{
			break;
		}
	}
}

/*****************************************************************************
* @brief   write disable.
*****************************************************************************/
void bsp_snor_write_disable(void)
{
	uint16_t cnt;

	cnt = 10;
	while (cnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_Write_Disable);

		if (0 == (bsp_snor_base_status_read() & SPI_NOR_ST_WRITE_EN))
		{
			break;
		}
	}
}

/*****************************************************************************
* @brief   Erase aector.       4k
*****************************************************************************/
uint8_t bsp_snor_sector_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_Sector_Erase, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   Erase block.      32k
*****************************************************************************/
uint8_t bsp_snor_block_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_Block_Erase_32K, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   Erase block.      64k
*****************************************************************************/
uint8_t bsp_snor_block64_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_Block_Erase_64K, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   Erase chip.
*****************************************************************************/
uint8_t bsp_snor_chip_erase(void)
{
	bsp_snor_write_enable();

	bsp_snor_cmd_exe(SPI_NOR_CMD_Chip_Erase);

	return bsp_snor_wait_for_erase(300 * 1000);
}

/*****************************************************************************
* @brief   read data.
*****************************************************************************/
uint8_t bsp_snor_read(uint32_t addr, int32_t Len, uint8_t *Des)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_144);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_read(
		SPI_NOR_CMD_Quad_Read_QA,
		addr, QSPI_CMD_ADRLEN_3_BYTE,
		6, //dummy
		Des, Len);

	hal_snor_spi_cs(1);

	return 1;
}

/*****************************************************************************
* @brief   write data ,must be in same page.
*****************************************************************************/
uint8_t bsp_snor_write(uint32_t addr, int32_t Len, const uint8_t *Src)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_114);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_write(
		SPI_NOR_CMD_Quad_Page_Program,
		addr, QSPI_CMD_ADRLEN_3_BYTE,
		(uint8_t *)Src, Len);

	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_busy();
}

uint8_t bsp_snor_write_multi(uint32_t addr, int32_t Len, const uint8_t *Src)
{
	int32_t len_wr;
	uint8_t St = 0;

	// 页写
	while (Len > 0)
	{
		// 一次最大写入地址页内剩余字节数
		len_wr = SPI_NOR_PAGE_SIZE - (addr & SPI_NOR_PAGE_MASK);

		if (len_wr > Len)
			len_wr = Len;

		St |= bsp_snor_write(addr, len_wr, Src);

		addr += len_wr;
		Src += len_wr;
		Len -= len_wr;
	}

	return St;
}

/*****************************************************************************
* @brief   read security register data.
*****************************************************************************/
uint8_t bsp_snor_security_reg_read(uint32_t addr, int32_t Len, uint8_t *Des)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_read(
		SPI_NOR_CMD_Read_SecurityReg,
		addr, QSPI_CMD_ADRLEN_3_BYTE,
		8, //dummy
		Des, Len);

	hal_snor_spi_cs(1);

	return 1;
}

/*****************************************************************************
* @brief   write security register data.
*****************************************************************************/
uint8_t bsp_snor_security_reg_write(uint32_t addr, int32_t Len, const uint8_t *Src)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_write(
		SPI_NOR_CMD_Program_SecurityReg,
		addr, QSPI_CMD_ADRLEN_3_BYTE,
		(uint8_t *)Src, Len);

	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_busy();
}

/*****************************************************************************
* @brief   Erase aector.       4k
*****************************************************************************/
uint8_t bsp_snor_security_reg_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_Erase_SecurityReg, addr, QSPI_CMD_ADRLEN_3_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   entry a32.
*****************************************************************************/
uint8_t bsp_snor32_enter_a32_mode(void)
{
	int8_t scnt = 5;

	while (scnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_A32_Enter_A32);

		if ((bsp_snor_reg_get(2) & 0x01) == 0x01)
		{
			break;
		}
	}

	if (scnt < 0)
		return 0;

	return 1;
}

/*****************************************************************************
* @brief   entry a24.
*****************************************************************************/
uint8_t bsp_snor32_enter_a24_mode(void)
{
	int8_t scnt = 5;

	while (scnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_A32_Exit_A32);

		if ((bsp_snor_reg_get(2) & 0x01) == 0x00)
		{
			break;
		}
	}

	if (scnt < 0)
		return 0;

	return 1;
}

/*****************************************************************************
* @brief   get mb.
*****************************************************************************/
uint8_t bsp_snor32_addr_mb_get(void)
{
	char scnt = 5;
	uint8_t mb;

	while (scnt--)
	{
		mb = bsp_snor_cmd_get(SPI_NOR_CMD_A32_Get_Addr_MB);

		if (mb == bsp_snor_cmd_get(SPI_NOR_CMD_A32_Get_Addr_MB))
		{
			break;
		}
	}

	return mb;
}

/*****************************************************************************
* @brief   set mb.
*****************************************************************************/
uint8_t bsp_snor32_addr_mb_set(uint8_t mb)
{
	int8_t scnt = 5;

	while (scnt--)
	{
		bsp_snor_cmd_exe(SPI_NOR_CMD_SRWrite_Enable);

		bsp_snor_cmd_set(SPI_NOR_CMD_A32_Set_Addr_MB, mb);

		if (mb == bsp_snor32_addr_mb_get())
		{
			break;
		}
	}

	if (scnt < 0)
		return 0;

	return 1;
}
/*****************************************************************************
* @brief   Erase aector.       4k
*****************************************************************************/
uint8_t bsp_snor32_sector_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_A32_Sector_Erase, addr, QSPI_CMD_ADRLEN_4_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   Erase block.      32k
*****************************************************************************/
uint8_t bsp_snor32_block_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_A32_Block_Erase_32K, addr, QSPI_CMD_ADRLEN_4_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   Erase block.      64k
*****************************************************************************/
uint8_t bsp_snor32_block64_erase(uint32_t addr)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_111);
	hal_snor_spi_cs(0);
	bsp_qpi_cmd_multi_write(SPI_NOR_CMD_A32_Block_Erase_64K, addr, QSPI_CMD_ADRLEN_4_BYTE, 0, 0);
	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_erase(3000);
}

/*****************************************************************************
* @brief   read data.
*****************************************************************************/
uint8_t bsp_snor32_read(uint32_t addr, int32_t Len, uint8_t *Des)
{
	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_144);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_read(
		SPI_NOR_CMD_A32_Quad_Read_QA,
		addr, QSPI_CMD_ADRLEN_4_BYTE,
		6, //dummy
		Des, Len);

	hal_snor_spi_cs(1);

	return 1;
}

/*****************************************************************************
* @brief   write data ,must be in same page.
*****************************************************************************/
uint8_t bsp_snor32_write(uint32_t addr, int32_t Len, const uint8_t *Src)
{
	bsp_snor_write_enable();

	bsp_qpi_opt_mode(QSPI_OPERATE_MODE_114);

	hal_snor_spi_cs(0);

	bsp_qpi_addr_write(
		SPI_NOR_CMD_A32_Quad_Page_Program,
		addr, QSPI_CMD_ADRLEN_4_BYTE,
		(uint8_t *)Src, Len);

	hal_snor_spi_cs(1);

	return bsp_snor_wait_for_busy();
}

uint8_t bsp_snor32_write_multi(uint32_t addr, int32_t Len, const uint8_t *Src)
{
	int32_t len_wr;
	uint8_t St = 0;

	// 页写
	while (Len > 0)
	{
		// 一次最大写入地址页内剩余字节数
		len_wr = SPI_NOR_PAGE_SIZE - (addr & SPI_NOR_PAGE_MASK);

		if (len_wr > Len)
			len_wr = Len;

		St |= bsp_snor32_write(addr, len_wr, Src);

		addr += len_wr;
		Src += len_wr;
		Len -= len_wr;
	}

	return St;
}
